package p2pcdnsim.cdn;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.jdom.Element;

import p2pcdnsim.exception.SurrogateAlreadyInsertedException;
import p2pcdnsim.network.Node;
import p2pcdnsim.network.RouterNode;
import p2pcdnsim.xml.XMLHandler;

public class GreedyPlacement implements PlacementStrategy {

	private XMLHandler requests;
	private boolean sizeRequests;
	
	public GreedyPlacement(boolean sizeRequests, XMLHandler requests) {
		this.sizeRequests = sizeRequests;
		this.requests = requests;
	}
	
	@Override
	public void placeSurrogates(List<Surrogate> surrogates, CDN cdn)
			throws SurrogateAlreadyInsertedException {
		
		RouterNode[] nodes = cdn.getTopology().getNodes();
		
		// Define the loads
		Map<Long,Long>loads = new HashMap<Long,Long>();
		{
			for(Node node: nodes) loads.put(node.getNodeId(), 0L);
			Iterator<Element> i = requests.iterator();
			while (i.hasNext()) {
				Element linksTemp = i.next();
				long objectID  = Long.parseLong(linksTemp.getChild("VideoObject").getText());
				long nodeId = cdn.getNodeOfClinet(Long.parseLong(linksTemp.getChild("Client").getText()));
				long load = loads.get(nodeId);
				load += sizeRequests ? cdn.getOriginServer().contents.get(objectID).getLength(): 1;
				loads.put(nodeId, load);
				//System.out.println("Load on router "+nodeId+": "+load);
			}
		}

		//Place the surrogates
		for(Surrogate surrogate: surrogates) {
			long bestPlaceCost = Long.MAX_VALUE;
			RouterNode chosenNode = null;
			//for each potential site
			for(int potentialSite = 0; potentialSite<nodes.length; potentialSite++) {
				long placeCost;
				//System.out.println("Evaluating placement on node "+nodes[place].getNodeId());
				if(nodes[potentialSite].getNetworkLayer().getTransportLayer().getApplicationLayer().getApplicationProcess() != null) {
					placeCost = Long.MAX_VALUE;
				} else {
					placeCost = 0;
					for(Node node: nodes) {
						//System.out.println("Evaluating cost node "+node.getNodeId());
						long load = loads.get(node.getNodeId());
						//System.out.println("Load on router "+node.getNodeId()+": "+load);
						long costToOriginServer = load * (1+node.getNetworkLayer().getRouterTable().getDistance(cdn.getOriginServerAddress()));
						//System.out.print(costToOriginServer+" ");
						long costToClosestSurrogate = Long.MAX_VALUE;
						Iterator<Surrogate> it = cdn.getSurrogates().iterator();
						while(it.hasNext()) {
							long cost = load * (1+node.getNetworkLayer().getRouterTable().getDistance(cdn.getSurrogateNode(it.next()).getNetworkLayer().getDefaultAddress()));
							if(cost < costToClosestSurrogate) costToClosestSurrogate = cost;
						}
						//System.out.print(costToClosestSurrogate+" ");
						long costOnPlacement = load * (1+node.getNetworkLayer().getRouterTable().getDistance(nodes[potentialSite].getNetworkLayer().getDefaultAddress()));
						//System.out.print(costOnPlacement+" ");
						placeCost += Math.min(costOnPlacement, Math.min(costToOriginServer, costToClosestSurrogate));
						//System.out.println(totalCost[place]+" ");
					}
				}
				//System.out.println("Placement cost on node "+nodes[place].getNodeId()+": " +placeCost);
				if(placeCost < bestPlaceCost) {
					bestPlaceCost = placeCost;
					chosenNode = nodes[potentialSite];
				}
			}
			//System.out.println(chosenNode.getNodeId());
			cdn.placeSurrogate(surrogate, chosenNode);
		}	
	}

}
